package org.darcy.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.darcy.dao.BizArticleLookMapper;
import org.darcy.dao.BizArticleLoveMapper;
import org.darcy.dao.BizArticleMapper;
import org.darcy.dao.BizArticleTagsMapper;
import org.darcy.dao.BizCommentMapper;
import org.darcy.entity.BizArticle;
import org.darcy.entity.BizArticleLook;
import org.darcy.entity.BizArticleLove;
import org.darcy.entity.BizArticleTags;
import org.darcy.entity.BizComment;
import org.darcy.entity.BizTags;
import org.darcy.entity.SysUser;
import org.darcy.framework.blog.business.enums.ArticleStatusEnum;
import org.darcy.framework.blog.business.enums.CommentStatusEnum;
import org.darcy.framework.blog.business.enums.FileUploadType;
import org.darcy.framework.blog.business.enums.ResponseStatus;
import org.darcy.framework.blog.exception.BlogArticleException;
import org.darcy.framework.blog.exception.BlogException;
import org.darcy.framework.blog.file.FileUploader;
import org.darcy.framework.blog.file.entity.VirtualFile;
import org.darcy.framework.blog.holder.RequestHolder;
import org.darcy.framework.blog.plugin.file.GlobalFileUploader;
import org.darcy.framework.generic.GenericServiceImpl;
import org.darcy.framework.util.IpUtil;
import org.darcy.framework.util.SessionUtil;
import org.darcy.model.BizArticleModel;
import org.darcy.service.BizArticleService;
import org.darcy.service.BizArticleTagsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;

import lombok.extern.slf4j.Slf4j;

/**
 * 文章列表
 *
 */
@Slf4j
@Service
public class BizArticleServiceImpl extends GenericServiceImpl<BizArticle> implements BizArticleService {

	@Autowired
	private BizArticleMapper bizArticleMapper;
	@Autowired
	private BizArticleLoveMapper bizArticleLoveMapper;
	@Autowired
	private BizArticleLookMapper bizArticleLookMapper;
	@Autowired
	private BizArticleTagsMapper bizArticleTagsMapper;
	@Autowired
	private BizArticleTagsService articleTagsService;
	@Autowired
	private BizCommentMapper commentMapper;

	/**
	 * 分页查询
	 */
	@Override
	public IPage<BizArticleModel> query(BizArticleModel param) {
		Page p = new Page(param.getP(), param.getPageSize());
		IPage<BizArticleModel> page = bizArticleMapper.query(p, param);
		if (page.getRecords().isEmpty()) {
			return page;
		}

		List<Long> ids = page.getRecords().stream().map(BizArticleModel::getId).collect(Collectors.toList());
		List<BizArticleModel> listTag = bizArticleMapper.listTagsByArticleId(ids);
		Map<Long, BizArticleModel> tagMap = listTag.stream()
				.collect(Collectors.toMap(BizArticleModel::getId, a -> a, (k1, k2) -> k1));

		for (BizArticleModel bizArticle : page.getRecords()) {
			BizArticleModel tagArticle = tagMap.get(bizArticle.getId());
			if (null == tagArticle) {
				log.warn("文章[{}] 未绑定标签信息，或者已绑定的标签不存在！", bizArticle.getTitle());
			} else {
				bizArticle.setTags(tagArticle.getTags());
			}
			this.subquery(bizArticle);
		}

		return page;
	}

	/**
	 * 站长推荐
	 *
	 * @param pageSize
	 * @return
	 */
	@Override
	public List<BizArticleModel> listRecommended(int pageSize) {
		BizArticleModel vo = new BizArticleModel();
		vo.setRecommended(true);
		vo.setStatus(ArticleStatusEnum.PUBLISHED.getCode());
		vo.setPageSize(pageSize);
		IPage<BizArticleModel> pageInfo = this.query(vo);
		return null == pageInfo ? null : pageInfo.getRecords();
	}

	/**
	 * 近期文章
	 *
	 * @param pageSize
	 * @return
	 */
	@Override
	public List<BizArticleModel> listRecent(int pageSize) {
		BizArticleModel vo = new BizArticleModel();
		vo.setPageSize(pageSize);
		vo.setStatus(ArticleStatusEnum.PUBLISHED.getCode());
		IPage<BizArticleModel> pageInfo = this.query(vo);
		return null == pageInfo ? null : pageInfo.getRecords();
	}

	/**
	 * 随机文章
	 *
	 * @param pageSize
	 * @return
	 */
	@Override
	public List<BizArticleModel> listRandom(int pageSize) {
		BizArticleModel vo = new BizArticleModel();
		vo.setRandom(true);
		vo.setStatus(ArticleStatusEnum.PUBLISHED.getCode());
		vo.setPageSize(pageSize);
		IPage<BizArticleModel> pageInfo = this.query(vo);
		return null == pageInfo ? null : pageInfo.getRecords();
	}

	/**
	 * 根据某篇文章获取与该文章相关的文章<br>
	 * 搜索同类型、同标签下的文章
	 *
	 * @param pageSize
	 * @param article
	 * @return
	 */
	@Override
	public List<BizArticleModel> listRelatedArticle(int pageSize, BizArticleModel article) {
		if (null == article) {
			return listRandom(pageSize);
		}
		BizArticleModel vo = new BizArticleModel();
		vo.setStatus(ArticleStatusEnum.PUBLISHED.getCode());
		List<BizTags> tags = article.getTags();
		if (!CollectionUtils.isEmpty(tags)) {
			List<Long> tagIds = new ArrayList<>();
			for (BizTags tag : tags) {
				tagIds.add(tag.getId());
			}
			vo.setTagIds(tagIds);
		}
		vo.setTypeId(article.getTypeId());
		vo.setPageSize(pageSize);
		IPage<BizArticleModel> pageInfo = this.query(vo);
		return pageInfo.getRecords();
	}

	/**
	 * 获取上一篇和下一篇
	 *
	 * @return
	 */
	@Override
	public Map<String, BizArticleModel> getPrevAndNextArticles(Date insertTime) {
		insertTime = null == insertTime ? new Date() : insertTime;
		List<BizArticleModel> entityList = bizArticleMapper.getPrevAndNextArticles(insertTime);
		if (CollectionUtils.isEmpty(entityList)) {
			return null;
		}
		Map<String, BizArticleModel> resultMap = new HashMap<>();
		for (BizArticleModel entity : entityList) {
			if (entity.getCreateTime().getTime() < insertTime.getTime()) {
				resultMap.put("prev", entity);
			} else {
				resultMap.put("next", entity);
			}
		}
		return resultMap;
	}

	/**
	 * 文章点赞
	 *
	 * @param id
	 */
	@Override
	public void doPraise(Long id) {
		String ip = IpUtil.getRealIp(RequestHolder.getRequest());
		String key = ip + "_doPraise_" + id;
		SysUser user = SessionUtil.getUser();
		BizArticleLove love = new BizArticleLove();
		if (null != user) {
			love.setUserId(user.getId());
		}
		love.setArticleId(id);
		love.setUserIp(IpUtil.getRealIp(RequestHolder.getRequest()));
		love.setLoveTime(new Date());
		love.setCreateTime(new Date());
		love.setUpdateTime(new Date());
		bizArticleLoveMapper.insert(love);
	}

	/**
	 * 是否存在文章
	 *
	 * @param id
	 * @return
	 */
	@Override
	public boolean isExist(Long id) {
		Integer count = bizArticleMapper.isExist(id);
		return count != null && count > 0;
	}

	/**
	 * 发布文章
	 *
	 * @param article
	 * @param tags
	 * @param file
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean publish(BizArticle article, Long[] tags, MultipartFile file) {
		if (null == tags || tags.length <= 0) {
			throw new BlogArticleException("请至少选择一个标签");
		}
		if (null != file) {
			FileUploader uploader = new GlobalFileUploader();
			VirtualFile virtualFile = uploader.upload(file, FileUploadType.COVER_IMAGE.getPath(), true);
			// 保存封面图片
			article.setCoverImage(virtualFile.getFilePath());
		}
		Long articleId = null;
		if ((articleId = article.getId()) != null) {
			this.update(article);
		} else {
			article.setUserId(SessionUtil.getUser().getId());
			articleId = this.insert(article).getId();
		}
		if (articleId != null) {
			articleTagsService.removeByArticleId(articleId);
			articleTagsService.insertList(tags, articleId);
		}
		return true;
	}

	/**
	 * 修改置顶、推荐
	 *
	 * @param type
	 * @param id
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean updateTopOrRecommendedById(String type, Long id) {
		BizArticle article = bizArticleMapper.selectById(id);
		article.setId(id);
		if ("top".equals(type)) {
			article.setTop(!article.getTop());
		} else if ("recommend".equals(type)) {
			article.setRecommended(!article.getRecommended());
		} else if ("comment".equals(type)) {
			article.setComment(!article.getComment());
		} else {
			throw new BlogException(ResponseStatus.INVALID_PARAMS.getMessage());
		}
		article.setUpdateTime(new Date());
		return bizArticleMapper.updateById(article) > 0;
	}

	@Override
	public void batchUpdateStatus(Long[] ids, boolean status) {
		if (ids == null || ids.length <= 0) {
			return;
		}
		bizArticleMapper.batchUpdateStatus(Arrays.asList(ids), status);
	}

	/**
	 * 获取热门文章
	 *
	 * @return
	 */
	@Override
	public List<BizArticleModel> listHotArticle(int pageSize) {
		PageHelper.startPage(1, pageSize);
		List<BizArticleModel> entityList = bizArticleMapper.listHotArticle();
		return entityList;
	}

	@Transactional(rollbackFor = Exception.class)
	public int delete(Long Id) {
		int count = 0;
		count += bizArticleMapper.deleteById(Id);
		// 删除标签记录
		QueryWrapper<BizArticleTags> q = new QueryWrapper<BizArticleTags>();
		q.eq("article_id", Id);
		count += bizArticleTagsMapper.delete(q);
		// 删除查看记录
		QueryWrapper<BizArticleLook> q2 = new QueryWrapper<BizArticleLook>();
		q2.eq("article_id", Id);
		count += bizArticleLookMapper.delete(q2);
		// 删除赞记录
		QueryWrapper<BizArticleLove> q3 = new QueryWrapper<BizArticleLove>();
		q3.eq("article_id", Id);
		count += bizArticleLoveMapper.delete(q3);

		return count;
	}

	private void subquery(BizArticleModel entity) {
		Long Id = entity.getId();
		// 查看的次数
		BizArticleLook look = new BizArticleLook();
		look.setArticleId(Id);
		QueryWrapper<BizArticleLook> q = new QueryWrapper<BizArticleLook>();
		q.eq("article_id", Id);
		entity.setLookCount(bizArticleLookMapper.selectCount(q).intValue());

		// 评论数
		QueryWrapper<BizComment> q2 = new QueryWrapper<BizComment>();
		q2.eq("sid", Id).eq("status", CommentStatusEnum.APPROVED.toString());
		entity.setCommentCount(commentMapper.selectCount(q2).intValue());

		// 喜欢的次数
		QueryWrapper<BizArticleLove> q3 = new QueryWrapper<BizArticleLove>();
		q3.eq("article_id", Id);
		entity.setLoveCount(bizArticleLoveMapper.selectCount(q3).intValue());
	}

	@Override
	public BizArticleModel getDetail(Long id) {
		return bizArticleMapper.getDetail(id);
	}

}
